#pragma once
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include <functional>
#include "logger.hpp"

namespace im
{
// 服务注册客户端
class Registry
{
public:
    using ptr = std::shared_ptr<Registry>;
private:
    // 注意三个成员的声明顺序，是初始化初始次序的依据
    std::shared_ptr<etcd::Client> _client;
    std::shared_ptr<etcd::KeepAlive> _keep_alive;
    uint64_t _lease_id;
public:
    Registry(const std::string& etcd_host)
        : _client(std::make_shared<etcd::Client>(etcd_host))
        , _keep_alive(_client->leasekeepalive(3).get())
        , _lease_id(_keep_alive->Lease())
    {}
    ~Registry()
    {
        // 停止保活动作
        _keep_alive->Cancel();
    }
    // 其实就是put，起了一个registry业务名
    bool registry(const std::string& key, const std::string& val)
    {
        auto resp = _client->put(key, val, _lease_id).get();
        if (resp.is_ok() == false) {
            LOG_ERROR("注册数据失败: {}", resp.error_message());
            return false;
        }
        return true;
    }
};

// 服务发现客户端
class Discovery
{
public:
    using ptr = std::shared_ptr<Discovery>; // 方便外部使用Ptr作为变量类型，获取Discovery智能指针对象
    using NotifyCallback = std::function<void(std::string, std::string)>;
private:
    std::shared_ptr<etcd::Client> _client;
    std::shared_ptr<etcd::Watcher> _watcher;
    NotifyCallback _put_cb;
    NotifyCallback _del_cb;
public:
    Discovery(const std::string& etcd_host, const std::string& basedir, const NotifyCallback& put_cb, const NotifyCallback& del_cb)
        : _client(std::make_shared<etcd::Client>(etcd_host))
        , _put_cb(put_cb)
        , _del_cb(del_cb)
    {
        auto resp = _client->ls(basedir).get();
        if (resp.is_ok() == false) 
            LOG_ERROR("获取服务器信息数据失败: {}", resp.error_message());
        int sz = resp.keys().size();
        for (int i = 0; i < sz; i++)
            if (_put_cb) _put_cb(resp.key(i), resp.value(i).as_string());

        // 进行数据监控，如果数据发生改变，则调用回调函数进行处理
        _watcher = std::make_shared<etcd::Watcher>(*_client.get(), basedir, std::bind(&Discovery::callback, this, std::placeholders::_1), true);
    }
    ~Discovery()
    {
        _watcher->Cancel();
    }
private:
    void callback(const etcd::Response& resp)
    {
        if (resp.is_ok() == false) {
            LOG_ERROR("收到一个错误的事件通知: {}", resp.error_message());
            return;
        }
        for (const auto& ev : resp.events())
        {
            if (ev.event_type() == etcd::Event::EventType::PUT)
            {
                if (_put_cb) _put_cb(ev.kv().key(), ev.kv().as_string());
                LOG_DEBUG("新增服务: {}-{}", ev.kv().key(), ev.kv().as_string());
            }
            if (ev.event_type() == etcd::Event::EventType::DELETE_)
            {
                if (_del_cb) _del_cb(ev.prev_kv().key(), ev.prev_kv().as_string());
                LOG_DEBUG("下线服务: {}-{}", ev.prev_kv().key(), ev.prev_kv().as_string());
            }
        }
    }
};
}